﻿using UnityEngine;
using System;
using System.Linq;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;

namespace UnityExtension
{
	public static class ReflectionUtility
	{
		/// <summary>
		/// 缓存每个类型公共的字段
		/// </summary>
		private static readonly Dictionary<Type,FieldInfo[]> fieldInfoLookup;
		/// <summary>
		/// 缓存每个类型可序列化的字段
		/// </summary>
		private static readonly Dictionary<Type,FieldInfo[]> fieldSerializeInfoLookup;
		/// <summary>
		/// 缓存每个类型可序列化的字段 包含基类
		/// </summary>
		private static readonly Dictionary<Type,FieldInfo[]> fieldSerializeAllInfoLookup;
		/// <summary>
		/// 每个类型可序列化的私有字段
		/// </summary>
		private static readonly Dictionary<Type,FieldInfo[]> fieldSerializePrivateInfoLookup;
		private static readonly Dictionary<Type,PropertyInfo[]> propertyInfoLookup;

		static ReflectionUtility()
		{
			ReflectionUtility.fieldInfoLookup = new Dictionary<Type,FieldInfo[]>();
			ReflectionUtility.fieldSerializeInfoLookup = new Dictionary<Type,FieldInfo[]>();
			ReflectionUtility.fieldSerializeAllInfoLookup = new Dictionary<Type,FieldInfo[]>();
			ReflectionUtility.fieldSerializePrivateInfoLookup = new Dictionary<Type,FieldInfo[]>();
			ReflectionUtility.propertyInfoLookup = new Dictionary<Type,PropertyInfo[]>();
		}
		public static void Clear()
		{
			fieldSerializeAllInfoLookup.Clear();
			fieldSerializeInfoLookup.Clear();
			fieldSerializePrivateInfoLookup.Clear();
		}
		public static string[] GetAllComponentNames()
		{
			IEnumerable<Type> types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(assembly => assembly.GetTypes()).Where(type => type.IsSubclassOf(typeof(Component)));
			return types.Select(x => x.FullName).ToArray();
		}

		public static string[] GetFieldNames(this Type type)
		{
			FieldInfo[] fields = type.GetAllFields(BindingFlags.Public | BindingFlags.Instance).ToArray();
			//fields = fields.Where (x => FsmUtility.GetVariableType (x.FieldType) != null).ToArray();
			return fields.Select(x => x.Name).ToArray();
		}

		public static string[] GetPropertyNames(this Type type,bool requiresWrite)
		{
			PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).ToArray();
			if(requiresWrite)
			{
				//properties= properties.Where (x => x.CanWrite && FsmUtility.GetVariableType(x.PropertyType) != null).ToArray ();			
			}
			return properties.Select(x => x.Name).ToArray();
		}

		public static string[] GetPropertyAndFieldNames(this Type type,bool requiresWrite)
		{
			List<string> names = new List<string>(type.GetPropertyNames(requiresWrite));
			names.AddRange(type.GetFieldNames());
			return names.ToArray();
		}

		public static string[] GetMethodNames(this Type type)
		{
			MethodInfo[] methods = type
				.GetMethods(BindingFlags.Public | BindingFlags.Instance)
					.ToArray();
			return methods.Where(y => y.GetParameters().Length == 0 && y.ReturnType == typeof(void)).Select(x => x.Name).ToArray();
		}

		public static FieldInfo[] GetAllFields(this Type type,BindingFlags flags)
		{
			if(type == null)
			{
				return new FieldInfo[0];
			}
			return type.GetFields(flags).Concat(GetAllFields(type.BaseType,flags)).ToArray();
		}

		public static FieldInfo[] GetPublicFields(this object obj)
		{
			return GetPublicFields(obj.GetType());
		}
		/// <summary>
		/// 取得可序列化的字段
		/// </summary>
		/// <param name="obj"></param>
		/// <returns></returns>
		public static FieldInfo[] GetSerializeFields(this UnityEngine.Object obj,bool includeBaseClass = false)
		{
			return GetSerializeFields(obj.GetType(),includeBaseClass);
		}

		public static FieldInfo[] GetPublicFields(this Type type)
		{
			if(!ReflectionUtility.fieldInfoLookup.ContainsKey(type))
			{
				fieldInfoLookup[type] = type.GetFields(BindingFlags.Instance | BindingFlags.Public);
			}

			return fieldInfoLookup[type];
		}
		/// <summary>
		/// 取得某个类型可序列化的字段
		/// </summary>
		/// <param name="type"></param>
		/// <param name="includeBaseClass">如果为true 则包含基类的可序列化字段</param>
		/// <returns></returns>
		public static FieldInfo[] GetSerializeFields(this Type type,bool includeBaseClass = false)
		{
			Dictionary<Type,FieldInfo[]> dis = includeBaseClass ? fieldSerializeAllInfoLookup : fieldSerializeInfoLookup;
			if(!dis.ContainsKey(type))
			{
				//Fil = type.GetFields(BindingFlags.Instance | BindingFlags.Public);
				FieldInfo[] privateInfos = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);//fieldSerializeInfoLookup[type]
				List<FieldInfo> infos = new List<FieldInfo>();
				foreach(var item in privateInfos)
				{
					//if(item.IsSerialized())
					{
						infos.Add(item);
					}
				}
				if(includeBaseClass)
				{
					if(type.BaseType != typeof(System.Object))
					{
						infos.AddRange(type.BaseType.GetSerializePrivateFields(true));
					}
				}
				dis[type] = infos.ToArray();
			}
			return dis[type];
		}
		public static FieldInfo[] GetSerializePrivateFields(this Type type,bool includeBaseClass = true)
		{
			if(!fieldSerializePrivateInfoLookup.ContainsKey(type))
			{
				FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
				List<FieldInfo> infos = new List<FieldInfo>(0);
				foreach(var item in fields)
				{
					if(item.IsPrivate)
					{
						//if(item.IsSerialized())
						{
							infos.Add(item);
						}
					}
				}
				if(includeBaseClass)
				{
					if(type.BaseType != typeof(System.Object))
					{
						infos.AddRange(type.BaseType.GetSerializePrivateFields());
					}
				}
				fieldSerializePrivateInfoLookup[type] = infos.ToArray();
			}
			return fieldSerializePrivateInfoLookup[type];
		}
		public static PropertyInfo[] GetPublicProperties(this object obj)
		{
			return GetPublicProperties(obj.GetType());
		}

		/// <summary>
		/// 递归root实例所有的T派生类实例
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="root"></param>
		/// <param name="ifRecursionChild">如果检测器返回true 就递归实例字段</param>
		/// <param name="action">当低轨道实例字段调用的函数</param>
		/// <param name="recursionReference">如果为false 有ReferenceAttribute属性的字段 不会递归</param>
		public static void RecursionSubclass<T>(
			System.Object root,
			System.Func<T,bool> ifRecursionChild,
			System.Action<T> beforeAction,
			System.Action<T> action,
			bool recursionReference = false) where T:class,new()
		{
			if(root == null)
			{
				return;
			}
			FieldInfo[] fields = root.GetType().GetSerializeFields(true);//GetAllFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
			for(int i = 0;i < fields.Length;i++)
			{
				FieldInfo field = fields[i];
				if(!recursionReference)
				{
					//if(field.HasAttribute(typeof(ReferenceAttribute)))
					{
						continue;
					}
				}
				Type fieldType = field.FieldType;
				#region IsSubclassOf(typeof(T)
				if(field.FieldType.IsSubclassOf(typeof(T)))
				{
					T value = (T)field.GetValue(root);
					if(value != null)
					{
						if(beforeAction != null)
						{
							beforeAction(value);
						}
						if(ifRecursionChild == null)
						{
							RecursionSubclass<T>(value,null,beforeAction,action,recursionReference);
						}
						else if(ifRecursionChild(value))
						{
							RecursionSubclass<T>(value,ifRecursionChild,beforeAction,action,recursionReference);
						}
						if(action != null)
						{
							action(value);
						}
					}
				}
				#endregion
				#region 如果是数组
				else if(field.FieldType.IsArray)
				{
					var array = field.GetValue(root) as Array;
					Type elementType = field.FieldType.GetElementType();
					if(elementType.IsSubclassOf(typeof(T)))
					{
						foreach(T value in array)
						{
							if(value != null)
							{
								if(beforeAction != null)
								{
									beforeAction(value);
								}
								if(ifRecursionChild == null)
								{
									RecursionSubclass<T>(value,null,beforeAction,action,recursionReference);
								}
								else if(ifRecursionChild(value))
								{
									RecursionSubclass<T>(value,ifRecursionChild,beforeAction,action,recursionReference);
								}
								if(action != null)
								{
									action(value);
								}
							}
						}
					}
				}
				#endregion
				#region 如果是模板list
				else if(fieldType.IsGenericType)
				{
					Type listyp = typeof(List<>);
					Type genericType = fieldType.GetGenericTypeDefinition();
					if(listyp == genericType)
					{
						Type[] listargTypes = fieldType.GetGenericArguments();
						foreach(var item in listargTypes)
						{
							if(item.IsSubclassOf(typeof(T)))
							{
								System.Object obj = field.GetValue(root);
								if(obj == null)
								{
									break;
								}
								IList vlist = obj as IList;
								if(vlist == null)
								{
									break;
								}
								foreach(var item11 in vlist)
								{
									T value = item11 as T;
									if(value != null)
									{
										if(beforeAction != null)
										{
											beforeAction(value);
										}
										if(ifRecursionChild == null)
										{
											RecursionSubclass<T>(value,null,beforeAction,action,recursionReference);
										}
										else if(ifRecursionChild(value))
										{
											RecursionSubclass<T>(value,ifRecursionChild,beforeAction,action,recursionReference);
										}
										if(action != null)
										{
											action(value);
										}
									}
								}
							}
						}
					}
				}
				#endregion
			}
		}
		public static void GetAllReferenceInstance<T>(List<T> list,System.Object root,bool recursionReference = false) where T:class,new()
		{
			if(list == null)
			{
				return;
			}
			RecursionSubclass<T>(root,
				(T item2) =>
				{
					return !list.Contains(item2);
				},null
				,(T item1) =>
				{
					if(!list.Contains(item1))
					{
						list.Add(item1);
					}
				},recursionReference);
		}
		public static PropertyInfo[] GetPublicProperties(this Type type)
		{
			if(!ReflectionUtility.propertyInfoLookup.ContainsKey(type))
			{
				propertyInfoLookup[type] = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
			}

			return propertyInfoLookup[type];
		}

		public static FieldInfo[] GetFields(this Type type)
		{
#if NETFX_CORE
			return type.GetRuntimeFields ().ToArray();
#else
			return type.GetFields();
#endif
		}

		public static FieldInfo GetField(this Type type,string name)
		{
#if NETFX_CORE
			return type.GetRuntimeField (name);
#else
			return type.GetField(name);
#endif
		}

		public static PropertyInfo GetProperty(this Type type,string name)
		{
#if NETFX_CORE
			return type.GetRuntimeProperty (name);
#else
			return type.GetProperty(name);
#endif
		}

		public static bool IsSubclassOf(this Type type,Type c)
		{
#if NETFX_CORE
			return type.GetTypeInfo().IsSubclassOf(c);
#else
			return type.IsSubclassOf(c);
#endif
		}
	}
}